/*
 * Copyright (c)  2015~2020, xforceplus
 * All rights reserved.
 * Project:datacare-orm
 * Id: AbstractInsertSqlManager.java   2020-10-10 23-38-14
 * Author: Evan
 */
package cn.datacare.orm.manager.impl;

import cn.datacare.orm.concurrent.CaseInsensitiveOrderSet;
import cn.datacare.orm.database.ClassDesc;
import cn.datacare.orm.database.KeyHolder;
import cn.datacare.orm.database.TableDesc;
import cn.datacare.orm.exception.OrmCoreException;
import cn.datacare.orm.manager.InsertSqlManager;
import cn.datacare.orm.source.SQLScript;
import cn.datacare.orm.util.kit.BeanKit;

import java.lang.reflect.Method;
import java.util.*;

import static cn.datacare.orm.constant.SqlSourceType.INSERT;
import static cn.datacare.orm.constant.SqlSourceType.INSERT_TEMPLATE;
import static cn.datacare.orm.exception.OrmErrorCode.ID_EXPECTED_ONE_ERROR_6013;


/**
 * <p>
 * Title:
 * </p>
 * <p>
 * Description:
 * </p>
 * <p>
 * Copyright: 2015~2020
 * </p>
 * <p>
 * Company/Department: xforceplus
 * </p>
 *
 * @author Evan
 * <b>Creation Time:</b> 2020-10-10 23-38-14
 * @since V1.0
 */
public abstract class AbstractInsertSqlManager extends AbstractSqlManager implements InsertSqlManager {


    /**
     * @param bean
     * @param id
     */
    protected void assignAutoId(Object bean, Object id) {
        Class target = bean.getClass();
        String table = this.nameConversion.getTableName(target);
        ClassDesc desc = this.metaDataManager.getTable(table).getClassDesc(target, this.nameConversion);

        if (desc.getIdCols().isEmpty()) {
            return;
        } else {
            Method getterMethod = (Method) desc.getIdMethods().get(desc.getIdAttrs().get(0));

            String name = getterMethod.getName();
            String setterName = name.replaceFirst("get", "set");
            try {
                Method setterMethod = target.getMethod(setterName, new Class[]{getterMethod.getReturnType()});
                Object value = BeanKit.convertValueToRequiredType(id, getterMethod.getReturnType());
                setterMethod.invoke(bean, new Object[]{value});
            } catch (Exception ex) {
                throw new UnsupportedOperationException("autoAssignKey failure " + ex.getMessage());
            }
        }
    }

    /**
     * 通用插入操作
     *
     * @param obj
     * @return int insert rows
     */
    @Override
    public int insert(Object obj) {
        return this.insert(obj.getClass(), obj, false);
    }

    /**
     * 插入实体
     *
     * @param obj     实体类
     * @param autoKey 是否自动从数据库获取主键值, 自增或者序列
     * @return int 插入行数
     */
    @Override
    public int insert(Object obj, boolean autoKey) {
        return this.insert(obj.getClass(),obj,autoKey);
    }

    /**
     * 插入实体
     *
     * @param clazz
     * @param paras
     * @param autoKey
     * @return 插入行数
     */
    @Override
    public int insert(Class<?> clazz, Object paras, boolean autoKey) {
        return this.generalInsert(clazz, paras, autoKey, false);
    }

    /**
     * @param obj
     * @return 插入行数
     */
    @Override
    public int insertTemplate(Object obj) {
        return this.insertTemplate(obj, false);
    }


    /**
     * 模板插入，并根据autoAssignKey 自动获取自增主键值
     *
     * @param clazz
     * @param params
     * @param autoKey
     * @return int 插入行数
     */
    @Override
    public int insertTemplate(Class clazz, Object params, boolean autoKey) {
        return this.generalInsert(clazz,params,autoKey,true);
    }

    /**
     * 模板插入，并根据autoAssignKey 自动获取自增主键值
     *
     * @param params
     * @param autoKey
     * @return int 插入行数
     */
    @Override
    public int insertTemplate(Object params, boolean autoKey) {
        return this.insertTemplate(params.getClass(), params, autoKey);
    }

    /**
     * 插入对象通用的方法，如果数据表有自增主键，需要获取到自增主键，参考使用 insert(Object paras,boolean
     *
     * @param clazz
     * @param obj
     * @return 插入行数
     */
    @Override
    public int insert(Class<?> clazz, Object obj) {
        return this.insert(clazz, obj, false);
    }

    /**
     * 抽像插入
     * @param clazz
     * @param params
     * @param autoKey
     * @param template
     * @return int 插入行数
     */
    @Override
    public int generalInsert(Class clazz, Object params, boolean autoKey, boolean template) {
        if (autoKey) {
            KeyHolder holder = new KeyHolder();
            int result = template ? this.insertTemplate(clazz, params, holder) : this.insert(clazz, params, holder);
            assignAutoId(params, holder.getKey());
            return result;

        }
        SQLScript script = this.getScript(clazz, template ? INSERT_TEMPLATE : INSERT);
        return script.insert(params);
    }

    /**
     * 批量保存
     *
     * @param clazz
     * @param list
     * @return 插入行数
     */
    @Override
    public int[] insertBatch(Class<?> clazz, List<?> list) {
        return this.insertBatch(clazz,list,false);
    }



    /**
     *  批量保存数据
     * @param clazz
     * @param list
     * @param autoKey
     * @return 插入行数
     */
    @Override
    public int[] insertBatch(Class<?> clazz, List<?> list, boolean autoKey) {
        SQLScript script = super.getScript(clazz, INSERT);
        LinkedList keys = new LinkedList();
        int[] ret = script.insertBatch(list, keys, autoKey);
        if (!autoKey&&keys.isEmpty()) {
            return ret;
        }
        if(!autoKey){
            return ret;
        }
        //如果有自增主键
        Iterator<?> it = list.iterator();
        Iterator keyIt = keys.iterator();
        while (it.hasNext()) {
            Object bean = it.next();
            keyIt.hasNext();
            Object key = keyIt.next();
            this.assignAutoId(bean, key);
        }
        return ret;
    }


    /**
     * 插入，并获取自增主键的值
     *
     * @param clazz
     * @param obj
     * @param holder 自增或者序列主健放到holder对象里
     * @return 插入行数
     */
    @Override
    public int insert(Class<?> clazz, Object obj, KeyHolder holder) {
        SQLScript sqlScript=this.getScript(clazz,INSERT);
        return sqlScript.insert(obj,holder);
    }

    /**
     * 模板插入，仅仅插入非空属性，并获取自增主键
     *
     * @param clazz
     * @param obj
     * @param holder
     * @return 插入行
     */
    @Override
    public int insertTemplate(Class<?> clazz, Object obj, KeyHolder holder) {
        SQLScript script = getScript(clazz, INSERT_TEMPLATE);
        return script.insert(obj, holder);
    }


    /**
     * 插入，并获取主键
     *
     * @param sqlId
     * @param params
     * @param holder
     * @param keyName
     * @return 插入影响行数
     */
    @Override
    public int insert(String sqlId, Object params, KeyHolder holder, String keyName) {
        SQLScript script = this.getScript(sqlId);
        return script.insertBySqlId(this.setParams(params), holder, keyName);
    }

    /**
     * 插入，并获取主键,主键将通过paras所代表的表名来获取
     *
     * @param sqlId
     * @param obj
     * @param holder
     * @return 插入行数
     */
    @Override
    public int insert(String sqlId, Object obj, KeyHolder holder){
        SQLScript script = getScript(sqlId);
        Map<String, Object> map =this.setParams(obj);
        if (holder != null) {
            TableDesc table=this.getTableDesc(obj.getClass());
            Set<String> idCols = table.getIdNames();
            if (idCols.size() != 1) {
                throw new OrmCoreException(ID_EXPECTED_ONE_ERROR_6013);
            }
            return script.insertBySqlId(map, holder, ((CaseInsensitiveOrderSet) idCols).getFirst());
        } else {
            return script.insertBySqlId(map, null, null);
        }
    }

    /**
     * 插入操作，数据库自增主键放到keyHolder里
     *
     * @param sqlId
     * @param clazz
     * @param params
     * @param holder
     * @return int 插入影响行数
     */
    @Override
    public int insert(String sqlId, Class<?> clazz, Map<String, Object> params, KeyHolder holder) {
        return 0;
    }

    /**
     * 插入，并获取自增主键值，因为此接口并未指定实体对象，因此需要keyName来指明数据库主键列
     *
     * @param sqlId
     * @param params
     * @param keyHolder KeyHolder
     * @param keyName   主键列名称
     * @return
     */
    @Override
    public int insert(String sqlId, Map<String, Object> params, KeyHolder keyHolder, String keyName) {
        return 0;
    }
}
